﻿#ifndef ___PCLIB_S_LOADLIB___
#define ___PCLIB_S_LOADLIB___


#include "../m_core/m_core.hpp"

//调用函数快捷示例
#ifndef CALLF
#define CALLF(H, F) H.GetFunctionPtr<decltype(F)*>(#F)
#endif

/**
*@brief      跨平台的动态库加载封装类。
*            注意PCDllHandle对象析构时，会释放动态库。
*/
class CPCDllHandle : CPCNoCopyable
{
public:
    explicit CPCDllHandle():m_Handle(NULL){}
    virtual ~CPCDllHandle()    { FreeLib(); }

    /**
    *@brief     加载当前模块文件路径下的动态库
    *@param     szDllName[in]           动态库名字。前面不要加lib，后面也不要加.so或.dll
    *@param     szSubDirName[in]        如果是当前文件夹，传入""。或者可选的子文件夹名字，名字前面不要加'/',后面要加'/’。例如 dirname/
    *@param     szFuncNames[in]         需要获取的函数名称列表，用|分割，如"func1|func2|fun3"
    *@param     bFromSelfDllPath[in]    false：从可执行文件位置加载（默认）；true从动态库文件自身加载
    *@return    成功：CPCResult.Succ    失败：CPCResult.ErrDesc[错误描述]
    *           如果部分函数未获取到，则也返回成功。同时未成功获取到的函数名称存放在std::string中
    */
    CPCResult<std::string> LoadModulePathLib(const char* szDllName, const char* szSubDirName, const char* szFuncNames, bool bFromSelfDllPath = false)
    {
        //参数检查
        CPCResult<std::string> result;
        if(szDllName == NULL || szDllName[0] == 0 || szSubDirName == NULL || 
            strlen(szDllName) >= MAX_PATH ||  strlen(szSubDirName) >= MAX_PATH)
        {
            return result.SetFail("dll Name(%s) is INVALID or sub dir(%s) is INVALID.", szDllName, szSubDirName);
        }

        //获取模块文件对应目录
        CPCResult<std::pair<std::string, std::string> > result2;
        if (bFromSelfDllPath)
        {
            result2 = CPCFileUtil::PCGetSelfFileDir();
        }
        else
        {
            result2 = CPCFileUtil::PCGetExecuteFileDir();
        }
        if(!result2)
        {
            return result.SetFail("Get Self Dir fail:%s",result2.ErrDesc());
        }

        //自动根据平台适配动态库名称
        char szDllFullPath[MAX_PATH];
#if defined(PCOS_WIN)
        snprintf(szDllFullPath, sizeof(szDllFullPath), "%s%s%s.dll",result2.Get().first.c_str(), szSubDirName, szDllName);
#else
        snprintf(szDllFullPath, sizeof(szDllFullPath), "%s%slib%s.so",result2.Get().first.c_str(), szSubDirName, szDllName);
#endif
        return LoadLib(szDllFullPath,szFuncNames);
    }

    /**
    *@brief     加载动态库。
    *@param     szPath          [in]    动态库路径/名称
    *@param     szFuncNames     [in]    需要获取的函数名称列表，用|分割，如"func1|func2|fun3"
    *@return    成功：CPCResult.Succ    失败：CPCResult.ErrDesc[错误描述]
    *           如果部分函数未获取到，则也返回成功。同时未成功获取到的函数名称存放在std::string中
    */
    CPCResult<std::string> LoadLib(const char* szPath, const char* szFuncNames)
    {
        CPCResult<std::string> result;
        m_Handle = NULL;
        if(szPath == NULL || szPath[0] == 0)
            return result.SetFail("dll full path is empty.");
#if defined(PCOS_WIN)
        m_Handle = LoadLibraryA(szPath);
        if(m_Handle == NULL)
        {
            return result.SetSystemFail();
        }
#else
        char * errorStr = NULL;
        dlerror();
        m_Handle = dlopen(szPath, RTLD_NOW);
        if( ((errorStr = dlerror()) != NULL) || m_Handle == NULL )
        {
            return result.SetFail("dlopen (%s) fail. err:%s",szPath, errorStr);
        }
#endif
        std::string strNotFindList; 
        m_addrs.clear();
        std::vector<std::string> vcFuncNames =  CPCStrUtil::PCStrSplit(szFuncNames,"|");
        for(std::vector<std::string>::iterator it = vcFuncNames.begin() ; it != vcFuncNames.end(); ++it)
        {
            if((*it).empty()) continue;

            //获取动态库中函数的地址
            void*    funcAddr = NULL;
            auto result2 = this->GetFuncAddr((*it).c_str(), funcAddr);
            if (!result2)
            {
                strNotFindList += (*it);
                strNotFindList += " not resolved. errinfo:(";
                strNotFindList += result.ErrDesc();
                strNotFindList += ") | ";
            }
            m_addrs.insert(std::make_pair((*it),funcAddr));
        }
        if(vcFuncNames.size() > 0 && m_addrs.size() == 0)
        {
            return result.SetFail("all symbol NOT resolved.:%s",szFuncNames);
        }
        return result.SetSucc(std::move(strNotFindList));
    }

    /**
    *@brief      是否加载成功
    */
    bool LoadSucc() const
    {
        return !m_addrs.empty();
    }

    /**
    *@brief      释放动态库，不必显式调用
    */
    void FreeLib()
    {
        if(m_Handle != NULL)
        {
#if defined(PCOS_WIN)
            FreeLibrary(m_Handle);
#else
            dlerror();
            dlclose(m_Handle);
#endif
            m_Handle = NULL;
        }
    }

    /**
    *@brief     单独获取某个函数地址，一般不必显式调用
    *@param     name        [in]    函数名字
    *@param     funcAddr    [out]   输出函数地址（成功时）
    *@return    是否成功
    */
    CPCResult<> GetFuncAddr(const char* name, void*& funcAddr)
    {
        CPCResult<> result;
        funcAddr = NULL;
        if (name == NULL || name[0] == 0)
        {
            return result.SetFail("func name is empty.");
        }
        if (m_Handle == NULL)
        {
            return result.SetFail("need load lib first.");
        }
#if defined(PCOS_WIN)
        funcAddr = GetProcAddress(m_Handle, name);
        if (funcAddr == NULL)
        {
            return result.SetSystemFail();
        }
#else
        char* errorStr = NULL;
        dlerror();
        funcAddr = dlsym(m_Handle, name);
        if (((errorStr = dlerror()) != NULL) || funcAddr == NULL)
        {
            return result.SetFail(errorStr);
        }
#endif
        return result;
    }

    /**
    *@brief     获取函数指针，调用示例：
    *                   例1：CALLF(handle, ZLOpenDevice)(ZL_CONNECTION_ETH, "1, 9600, n, 8, 1", -1);
    *                   例2：handle.GetFunctionPtr<decltype(ZLOpenDevice)*>("ZLOpenDevice")(ZL_CONNECTION_ETH, "1, 9600, n, 8, 1", -1);
    *@param     name        [in]    函数名字
    *@return    函数地址
    */
    template<typename F>
    F GetFunctionPtr(const char* name)
    {
        F func = (F) m_addrs[name];
        return func;
    }


private:
    //动态库句柄
#if defined(PCOS_WIN)
    HINSTANCE m_Handle;
#else
    void * m_Handle;
#endif
public:
    //函数【名称：地址】列表
    std::unordered_map<std::string,void*> m_addrs;
};


#endif    //___PCLIB_S_LOADLIB___







































/*
template<class Function, class... Args>
inline auto FuncWrapper(Function && f, Args && ... args) -> decltype(f(std::forward<Args>(args)...))
{
//typedef decltype(f(std::forward<Args>(args)...)) ReturnType;
return f(std::forward<Args>(args)...);
//your code; you can use the above typedef.
}

template <class T, class R, typename... Args>
class  MyDelegate
{
public:
    MyDelegate(T* t, R  (T::*f)(Args...) ):m_t(t),m_f(f) {}

    R operator()(Args&&... args)
    {
            return (m_t->*m_f)(std::forward<Args>(args) ...);
    }

private:
    T* m_t;
    R  (T::*m_f)(Args...);
};

template <class T, class R, typename... Args>
MyDelegate<T, R, Args...> CreateDelegate(T* t, R (T::*f)(Args...))
{
    return MyDelegate<T, R, Args...>(t, f);
}

struct A
{
    void Fun(int i){cout<<i<<endl;}
    void Fun1(int i, double j){cout<<i+j<<endl;}
};

int main()
{
    A a;
    auto d = CreateDelegate(&a, &A::Fun); //创建委托
    d(1); //调用委托，将输出1
    auto d1 = CreateDelegate(&a, &A::Fun1); //创建委托
    d1(1, 2.5); //调用委托，将输出3.5
}








enum CallLibType
{
    eStdCall,
    eCdecl,
    eFastCall
};

template<typename ResultType, typename ...params>
void CallLibrary(CallLibType eType, std::string strDll, std::string strFuncName, ResultType& ret, params... para)
{
    HMODULE hd = LoadLibraryA(strDll.c_str());

    if (0 == hd) return;
    if (eStdCall == eType)
    {
        typedef ResultType(__stdcall *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            ret = func(para...);
        }
    }
    else if (eCdecl == eType)
    {
        typedef ResultType(__cdecl *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            ret = func(para...);
        }
    }
    else if (eFastCall == eType)
    {
        typedef ResultType(__fastcall *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            ret = func(para...);
        }
    }

    FreeLibrary(hd);
}

template<typename ...params>
void CallLibrary(CallLibType eType, std::string strDll, std::string strFuncName, params... para)
{
    HMODULE hd = LoadLibraryA(strDll.c_str());

    if (0 == hd) return;
    if (eStdCall == eType)
    {
        typedef void(__stdcall *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            func(para...);
        }
    }
    else if (eCdecl == eType)
    {
        typedef void(__cdecl *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            func(para...);
        }
    }
    else if (eFastCall == eType)
    {
        typedef void(__fastcall *FuncType)(params...);
        FuncType func = (FuncType)GetProcAddress(hd, strFuncName.c_str());
        if (func != NULL)
        {
            func(para...);
        }
    }
    FreeLibrary(hd);
}

    */
